#ifndef FUNCTION_H_
#define FUNCTION_H_

#include "wifi.h"
#include "timer.h"

// ----------------------------------------------------------------//
// ----------------------------------------------------------------//
// ---------------------BASIC FUNCTION-----------------------------//
// ---------------------BASIC FUNCTION-----------------------------//
// ---------------------BASIC FUNCTION-----------------------------//
// ----------------------------------------------------------------//
// ----------------------------------------------------------------//
bool key_monitor_unit(uint8_t k)
{
    int state = digitalRead(k);
    if (state == LOW)
        return true;
    else
        return false;
}
void key_monitor()
{

    if (key_monitor_unit(KEY_MODE))
    {
        Serial.println("Mode is being pressed without setting");
    }

    if (key_monitor_unit(KEY_TYPE))
    {
        Serial.println("Type is being pressed");
        uint8_t code = static_cast<int>(control_type);
        code = (code + 1) % 3;
        control_type = static_cast<Control_type>(code);
    }
    if (key_monitor_unit(KEY_NEXT))
    {
        Serial.println("Next is being pressed");
        isMODEModified = true;
        uint8_t code = static_cast<int>(mode);
        code = (code + 1) % 9;
        mode = static_cast<Mode>(code);
    }
    if (key_monitor_unit(KEY_PREVIEW))
        Serial.println("Preview is being pressed without setting");
}

void handleSerialData()
{ // ture:have statistic to hand
    if (Serial.available())
    {
        int num = 0;

        String str = Serial.readString();
        num = str.toInt();
        if (num == 1)
        {
            int control = static_cast<int>(control_type);
            control = (control + 1) % 3;
            control_type = static_cast<Control_type>(control);
        }
        if (num == 2)
        {
            isMODEModified = true;
            int control = static_cast<int>(mode);
            control = (control + 1) % 9;
            mode = static_cast<Mode>(control);
        }

        Serial.printf("receive:%d\n", num);
    }
}

bool check()
{
    // hande commands
    Webctrl.handleRequest();
    handleSerialData();

    if (isMODEModified)
    {
        isMODEModified = false; // Reset
        return true;
    }
    else
        return false;
}

// when mode change we need to jump
bool delay_plus(unsigned long delay_time) // Return true to jump out of the loop
{
    unsigned long start_time = millis();
    while (millis() - start_time < delay_time)
    {
        if (check()) // If return false, the cycle could continue; if return true, jump out of loop
            return true;
        delay(1); // Delay in small increments to allow mode check
    }
    return false;
}

// bool delay_plus(unsigned long delay_time) // Return true to jump out of the loop
// {
//     if (check()) // If return false, the cycle could continue; if return true, jump out of loop
//         return true;

//     delay(delay_time); // Delay only if check() returns false
//     return false;
// }

void show()
{
    // need not clear
    for (uint8_t i = 0; i < NUM_LEDS; i++)
        leds[i] = CHSV(HUE[i], 255, BRIGHT[i]);
    FastLED.show();
}

class Hue
{
public:
    void setbg(uint8_t hue)
    {
        for (uint8_t v = 0; v < NUM_LEDS; v++)
            HUE[v] = hue;
    }
    void setsingle(int pos, uint8_t hue)
    {
        if (pos < NUM_LEDS && pos >= 0)
        {
            HUE[pos] = hue;
        }
    }
    void set5point(uint8_t pos, uint8_t hue)
    {
        uint8_t step = NUM_LEDS / 5;
        for (uint8_t i = 0; i < 5; i++)
            setsingle(pos + i * step, hue);
    }
    void setanypoint(uint8_t num, uint8_t pos, uint8_t hue)
    {
        for (uint8_t i = 0; i < num; i++)
            setsingle(pos - i, hue);
    }
};

class Bright
{
public:
    void all()
    {
        for (uint8_t v = 0; v < NUM_LEDS; v++)
            BRIGHT[v] = 255;
    }

    void setbg(uint8_t brightness)
    {
        for (uint8_t v = 0; v < NUM_LEDS; v++)
            BRIGHT[v] = brightness;
    }
    void setsingle(int pos, uint8_t brightness)
    {
        if (pos < NUM_LEDS && pos >= 0)
        {
            BRIGHT[pos] = brightness;
        }
    }
    void set5point(uint8_t pos, uint8_t brightness)
    {
        uint8_t step = NUM_LEDS / 5;
        for (uint8_t i = 0; i < 5; i++)
        {
            uint8_t index = pos + i * step;
            if (index < NUM_LEDS)
            {
                setsingle(index, brightness);
            }
        }
    }

    void hightolow(uint8_t num, uint8_t pos, Hightolow_type mode) // for n leds
    {

        float rate = (MaxValue - MinValue) / num;
        switch (mode)
        {
        case GRADIENT: // mode 1: gradient to low : Lpos>Lpos-1>Lpos-2>...>Lpos-num
            for (int i = 0; i <= num; i++)
            {
                int index = pos - i;
                if (index >= 0 && index < NUM_LEDS)
                { // Ensure index is within valid range
                    uint8_t brightness = (uint8_t)(MaxValue - i * rate);
                    setsingle(index, brightness);
                }
            }
            break;
        case DROP:                    // mode 2: drop to low : Lpos>Lpos-1=Lpos-2=...=Lpos-num
            setsingle(pos, MaxValue); // draw in 1st
            for (uint8_t i = 1; i < num; i++)
            {
                int index = pos - i;
                if (index >= 0 && index < NUM_LEDS)
                { // Ensure index is within valid range
                    setsingle(index, MinValue);
                }
            }

            break;
        }
    }

    void gradient(Gradient_type type)
    {
        uint8_t delay_time = Delay_Time / 2;
        switch (type)
        {
        case DOWN: // to black
            for (uint8_t i = 0; i <= max_bright; i++)
            {
                FastLED.setBrightness(max_bright - i);
                FastLED.show();
                delay_plus(delay_time / 5);
            }
            break;
        case UP:
            for (uint8_t i = 0; i <= max_bright; i++)
            {
                FastLED.setBrightness(i);
                FastLED.show();
                delay_plus(delay_time / 5);
            }
            break;
        }
    }

    void breath()
    {
        uint8_t delay_time = Delay_Time / 6;
        bool whetherexit = false;
        while (true)
        {
            // breath down
            for (uint8_t i = 0; i <= max_bright - min_bright; i++)
            {
                FastLED.setBrightness(max_bright - i);
                FastLED.show();
                if (delay_plus(delay_time))
                {
                    whetherexit = true;
                    break;
                }
            }
            if (whetherexit)
                break;
            delay_plus(Delay_Time * 3); // extend all-off time

            // breath up
            for (uint8_t i = 0; i <= max_bright - min_bright; i++)
            {
                FastLED.setBrightness(min_bright + i);
                FastLED.show();
                if (delay_plus(delay_time))
                {
                    whetherexit = true;
                    break;
                }
            }
            if (whetherexit)
                break;

            delay_plus(Delay_Time * 5); // extend all-bright time
        }
    }

    void clear()
    {
        for (uint8_t i = 0; i < NUM_LEDS; i++)
            BRIGHT[i] = 0;
    }
};

// ----------------------------------------------------------------//
// ----------------------------------------------------------------//
// -------------------------MODE-----------------------------------//
// -------------------------MODE-----------------------------------//
// -------------------------MODE-----------------------------------//
// ----------------------------------------------------------------//
// ----------------------------------------------------------------//

Bright Brightctrl; // build the controler
Hue Huectrl;

void fivepointflow()
{
    FastLED.setBrightness(max_bright);
    uint8_t pos[5] = {};
    uint8_t hue[5] = {};

    // Initialize position and hue
    for (uint8_t i = 0; i < 5; i++)
    {
        pos[i] = i * NUM_LEDS / 5;
        hue[i] = i * 256 / 5;
    }

    while (true)
    {
        // Turn on LEDs with specified brightness and hue
        for (uint8_t i = 0; i < 5; i++)
        {
            Brightctrl.setsingle(pos[i], MaxValue);
            HUE[pos[i]] = hue[i];
        }
        show(); // Update the new setting

        // Check if delay_plus returns true, then break the loop
        if (delay_plus(Delay_Time))
            break;

        // Turn off the previous LEDs
        for (uint8_t i = 0; i < 5; i++)
            Brightctrl.setsingle(pos[i], 0); // Turn off the previous LED

        // Update position and hue for the next cycle
        for (uint8_t i = 0; i < 5; i++)
        {
            pos[i] = (pos[i] + 1) % NUM_LEDS; // Move to the next LED
            hue[i] = (hue[i] + 1) % 256;      // Change the hue
        }
    }
}
void launch_bg() //////
{
    Brightctrl.clear();
    FastLED.setBrightness(max_bright);
    // set background
    Brightctrl.setbg(MinValue);
    Huectrl.setbg(hue[1]);
    // set single and circle

    bool whetherexit = false;
    while (true)
    {
        for (uint8_t i = 0; i < NUM_LEDS; i++)
        {
            Brightctrl.setsingle(i, MaxValue);
            Huectrl.setsingle(i, hue[3]);
            show();
            if (delay_plus(Delay_Time))
            {
                whetherexit = true;
                break;
            }
            // set to back
            Brightctrl.setsingle(i, MinValue);
            Huectrl.setsingle(i, hue[1]);
        }
        if (whetherexit)
            break;
    }
}

void launch_cs()
{
    Brightctrl.clear();
    FastLED.setBrightness(max_bright);

    Huectrl.setbg(hue[4]); // set color
    for (uint8_t pos = 0; pos < NUM_LEDS + 5; pos++)
    {
        Brightctrl.hightolow(5, pos, GRADIENT);
        show();
        delay_plus(Delay_Time);
        Brightctrl.clear(); // make the belt keep off
    }
}

void launch_as()
{
    Brightctrl.clear();
    FastLED.setBrightness(max_bright);

    Huectrl.setbg(hue[5]);
    Serial.println("hue5=" + hue[5]);
    for (uint8_t pos = 0; pos < NUM_LEDS + 5; pos++)
    {
        float factor = 1.0 - sin((float)pos / (NUM_LEDS + 5));    // 计算位置比例
        uint8_t delay_time = (uint8_t)(factor * Delay_Time) + 10; // 根据比例调整延迟时间

        Brightctrl.hightolow(5, pos, GRADIENT);
        show();
        if (delay_plus(delay_time))
            break;
        Brightctrl.clear(); // make the belt keep off
    }
}
void skyarrow_single()
{
    Brightctrl.clear();
    FastLED.setBrightness(max_bright);
    // set hue
    Huectrl.setbg(hue[0]);

    uint8_t numtoshow = 5;
    for (uint8_t i = 0; i < NUM_LEDS + numtoshow; i++)
    {
        Brightctrl.hightolow(numtoshow, i, GRADIENT);
        // control pace
        show();
        if (delay_plus(Delay_Time))
            break;
    }
}
void skyarrow_color()
{
    Brightctrl.clear();
    FastLED.setBrightness(max_bright);
    uint8_t num_points = 5;

    // set hue
    for (uint8_t i = 0; i <= NUM_LEDS; i++) // fill rainbow
        HUE[NUM_LEDS - i] = 0 + i * 10;

    for (uint8_t i = 0; i < NUM_LEDS + num_points; i++)
    {
        uint8_t num_head = 1 + i / num_points;
        for (uint8_t v = 0; v < num_head; v++)
            Brightctrl.hightolow(num_points, i - v * num_points, DROP);

        show();
        if (delay_plus(Delay_Time))
            break;
    }
}

void bloom() // With the background color and five point flow
{
    FastLED.setBrightness(max_bright);
    uint8_t num_points = 5; // Number of flowing points

    // Set background brightness and color
    Brightctrl.setbg(MinValue);
    for (uint8_t i = 0; i <= NUM_LEDS; i++) // Fill rainbow
        HUE[NUM_LEDS - i] = 0 + i * 10;

    uint8_t positions[num_points]; // Store positions of flowing points

    // Initialize positions array
    for (uint8_t i = 0; i < num_points; i++)
        positions[i] = 2 + i * (NUM_LEDS / num_points); // Evenly distribute flowing points

    while (true) // Infinite loop for continuous flow effect
    {
        for (uint8_t i = 0; i < num_points; i++)
            Brightctrl.setsingle(positions[i], MaxValue); // Set the brightness of the current flowing point

        show(); // Update display

        if (delay_plus(Delay_Time)) // Control flow speed, if return ture that jump
            break;                  // Exit loop

        for (uint8_t i = 0; i < num_points; i++)
        {
            Brightctrl.setsingle(positions[i], MinValue); // Reset the brightness of the previous positions
            positions[i] = (positions[i] + 1) % NUM_LEDS; // Update position to move forward
        }
    }
}

void rainbowgradient()
{
    FastLED.setBrightness(max_bright);
    // fillbright
    Brightctrl.all();

    uint8_t deltaHue = 10;
    bool whetherexit = false;
    while (true)
    {
        for (uint8_t beginHue = 0; beginHue < 256; beginHue++)
        {
            for (uint8_t i = 0; i <= NUM_LEDS; i++) // fill rainbow
                HUE[NUM_LEDS - i] = beginHue + i * deltaHue;

            show(); // Update

            if (delay_plus(Delay_Time / 6))
            {
                whetherexit = true;
                break;
            }
        }
        if (whetherexit)
            break;
    }
}

// ----------------------------------------------------------------//
// -------------------------MODE MANAGE----------------------------//
// ----------------------------------------------------------------//

void change(Mode mode)
{
    Brightctrl.gradient(DOWN); // gradient for change mode
    if (Breath != mode)        // clean the previous leds
        Brightctrl.clear();

    FastLED.setBrightness(0);
    bright = 0;
    // start the millsecond timer to gradient display
    timer_ms.attach_ms(2, timerhandlems);

    switch (mode)
    {

    case Skyarrow_Color:
        skyarrow_color();
        break;
    case Skyarrow_Single:
        skyarrow_single();
        break;
    case Bloom:
        bloom(); // 2
        break;

    case Launch_Cs:
        launch_cs(); // 4
        break;
    case Launch_As:
        launch_as(); // 5
        break;
    case Launch_Bg:
        launch_bg(/*hue[1], hue[3]*/);
        break;
    case Rainbowgradient:
        rainbowgradient();
        break;
    case Fivepointflow:
        fivepointflow();
        break;
    case Breath:
        Brightctrl.breath();
        break;
    default:
        Brightctrl.setbg(0);
        break;
    }
}

void play()
{
    switch (control_type)
    {
    case CIRCULATION:
        Serial.println("THISI IS CIRCULATION PLAY");
        if (!isTimerOn)
        {
            timer_seconds.attach(30, timerhandleseconds);
            isTimerOn = true;
        }

        break;

    case RANDOM:
        Serial.println("THISI IS RANDOM PLAY");
        if (!isTimerOn)
        {
            timer_seconds.attach(30, timerhandleseconds);
            isTimerOn = true;
        }
        break;
    case SINGLE:
        Serial.println("THISI IS SINGLE PLAY");
        if (isTimerOn)
        {
            timer_seconds.detach();
            isTimerOn = false;
            if (isTimerOn)
            {
                Serial.println("close timer false");
            }
        }
        break;
    default:
        control_type = CIRCULATION;
        break;
    }
    change(mode);
}

#endif